Best Meeting Point

A group of two or more people wants to meet and minimize the total travel distance. You are given a 2D grid of values 0 or 1, where each 1 marks the home of someone in the group. The distance is calculated using Manhattan Distance, where distance(p1, p2) = |p2.x - p1.x| + |p2.y - p1.y|.

For example, given three people living at (0,0), (0,4), and (2,2):

  1. 1 - 0 - 0 - 0 - 1
  2. | | | | |
  3. 0 - 0 - 0 - 0 - 0
  4. | | | | |
  5. 0 - 0 - 1 - 0 - 0

The point (0,2) is an ideal meeting point, as the total travel distance of 2+2+2=6 is minimal. So return 6.

Hint:

Try to solve it in one dimension first. How can this solution apply to the two dimension case?

Solution:

  1. import java.util.Random;
  2. public class Solution {
  3. public int minTotalDistance(int[][] grid) {
  4. List<Integer> x = new ArrayList<>();
  5. List<Integer> y = new ArrayList<>();
  6. for (int i = 0; i < grid.length; i++) {
  7. for (int j = 0; j < grid[0].length; j++) {
  8. if (grid[i][j] == 1) {
  9. x.add(i); y.add(j);
  10. }
  11. }
  12. }
  13. // get median of x[] and y[] using quick select
  14. int mx = x.get(quickSelect(x, 0, x.size() - 1, x.size() / 2 + 1));
  15. int my = y.get(quickSelect(y, 0, y.size() - 1, y.size() / 2 + 1));
  16. // calculate the total Manhattan distance
  17. int total = 0;
  18. for (int i = 0; i < x.size(); i++) {
  19. total += Math.abs(x.get(i) - mx) + Math.abs(y.get(i) - my);
  20. }
  21. return total;
  22. }
  23. // return the index of the kth smallest number
  24. // avg. O(n) time complexity
  25. int quickSelect(List<Integer> a, int lo, int hi, int k) {
  26. // use quick sort's idea
  27. // randomly pick a pivot and put it to a[hi]
  28. // we need to do this, otherwise quick sort is slow!
  29. Random rand = new Random();
  30. int p = lo + rand.nextInt(hi - lo + 1);
  31. Collections.swap(a, p, hi);
  32. // put nums that are <= pivot to the left
  33. // put nums that are > pivot to the right
  34. int i = lo, j = hi, pivot = a.get(hi);
  35. while (i < j) {
  36. if (a.get(i++) > pivot) Collections.swap(a, --i, --j);
  37. }
  38. Collections.swap(a, i, hi);
  39. // count the nums that are <= pivot from lo
  40. int m = i - lo + 1;
  41. // pivot is the one!
  42. if (m == k) return i;
  43. // pivot is too big, so it must be on the left
  44. else if (m > k) return quickSelect(a, lo, i - 1, k);
  45. // pivot is too small, so it must be on the right
  46. else return quickSelect(a, i + 1, hi, k - m);
  47. }
  48. }